/*
Matthew Parker
CPE 301
Design Assignment 5
1. Connect an LM34 temperature sensor to analog input pin and read the value.
2. Display the value every 1 second on the serial terminal. Use a timer with an interrupt.
3. Display the temperature on 4x7-segment display component
*/

#define F_CPU 8000000UL
//#define XTAL 16000000L
#include <inttypes.h>
#include <avr/io.h>
#include <util/delay.h>
//#include <stdio.h>
#define byte volatile unsigned char

const uint16_t baudrate = 9600;
//output digit to a 7-segment display, put into PORTB
//const unsigned char num[10] = {63, 6, 91, 79, 102, 109, 124, 7, 127, 103};
const unsigned char num[10] = {0b11101011, 0b10001000, 0b11010011, 0b11011010, 0b10111000, 0b01111010, 0b01111011, 0b11001000, 0b11111011, 0b11111010};
//7-segment display selector, put into PORTD
//const unsigned char led[4] = {0b11101111, 0b11110111, 0b11111011, 0b11111101};
const unsigned char led[4] = {0x10, 0x08, 0x04, 0x02};
const uint16_t digit_split[3] ={1000, 100, 10};

uint8_t readSensor(uint8_t pin){
	//Preserve the first 4 bits
	ADMUX	&=	0xF0;
	//change the last 4 bits to match the input being read from
	ADMUX	|=	pin;
	//start analog to digital conversion
	ADCSRA |= (1<<ADSC);
	while ( ((ADCSRA&(1<<ADIF))==0) || (ADCSRA&(1<<ADSC)) ){
		//wait for conversion to finish
	}
	return ADCH;
}

void usart_init(int sendMode) {
	if(sendMode){
		UCSR0B = (1<<TXEN0); //Send data enable
	} else {
		UCSR0B = (1<<RXEN0); //receive data enable
	}
	UCSR0C = (1<<UCSZ01) | (1<<UCSZ00);// | (1<<UMSEL00); //8-bit character size, (a)synchronous mode
	UBRR0 = F_CPU/16/baudrate-1;
}

void usart_send(uint8_t ch){
	//wait for buffer to be ready to write to
	while(!(UCSR0A &(1<<UDRE0)));
	UDR0 = ch;
}

void display(uint8_t high, uint8_t low){
	//Display a 4-digit number onto 4 x 7-segment display
	//the 4-digit number is a 3 digit integer + an 1-digit integer decimal place
	uint8_t i;
	for(i = 0; i <4; i++){
		PORTD = led[i];
		if(i < 3)
			PORTB = num[(high/digit_split[i])%10];
		else
			PORTB = num[low];
		if(i == 2)
			PORTB |= (1 << 2); // decimal point
		_delay_ms(5);
	}
}


int main(void)
{
	DDRB = 0xFF;
	DDRD = 0xFF;
	ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1); //enable ADC, divide clock by 64 for 125kHz sampling
	//REFS=01 for "AVcc with external capacitor on AREF pin" mode
	//Only going to use 8-bits, so we will read only the most significant bits and set them to be stored in ADCH
	//Whenever ADCH is read from, the ADC process is reset.
	ADMUX = (1 << REFS0) | (1 << ADLAR);
	byte temp = 0;
	byte temp2 = 0;
	byte i;
	usart_init(1);
	while(1){
		//read sensor connected to ADC0
		temp = readSensor(0);
		//0.1V = 0x05 in ADCH
		//1 Degree = 10mV
		//Quick conversion: Degrees = 2 * ADCH value
		//degrees F = 1.8C + 32
		//example: 12 in ADCH => 0.24V => 24 degrees C => 75.2 degrees F
		temp2 = (2*temp*18+320)%1000; // temp2 is the digit after the decimal point
		temp = (2*temp*18+320)/10; // temp is the temperature in Fahrenheit without the decimal point
		//output to the terminal
		usart_send(temp);
		_delay_ms(10);
		usart_send(temp2);
		//Display temperature on the 7-segment displays for about 1 second
		for (i=0; i<200; i++)
				display(temp, temp2);
	}
	return 0;
}